package com.thinkingcoder.tcdfs.controller;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.thinkingcoder.tcdfs.constant.CustomHeaderConstants;
import com.thinkingcoder.tcdfs.meta.FileMeta;
import com.thinkingcoder.tcdfs.properties.TcDfsProperties;
import com.thinkingcoder.tcdfs.syncer.HttpSyncer;
import com.thinkingcoder.tcdfs.syncer.MQSyncer;
import com.thinkingcoder.tcdfs.util.FileUtils;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

/**
 * file download and upload controller.
 */
@Slf4j
@RestController
public class FileController {

    @Autowired
    private TcDfsProperties tcDfsProperties;

    @Autowired
    HttpSyncer httpSyncer;

    @Autowired
    MQSyncer mqSyncer;

    @SneakyThrows
    @PostMapping("/upload")
    public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) {

        String hxf = request.getHeader(CustomHeaderConstants.FileName.X_FILENAME);
        String hxof = request.getHeader(CustomHeaderConstants.FileName.X_ORIGINAL_FILENAME);

        // 1. 处理文件
        boolean needSync = false;
        String filename = (hxf == null || hxf.isEmpty()) ? null : URLDecoder.decode(hxf, StandardCharsets.UTF_8);
        String originalFilename = file.getOriginalFilename();
        if (filename == null || filename.isEmpty()) { // 如果为空则是正常上传
            needSync = true;
            filename = FileUtils.getUUIDFileName(originalFilename);
        } else { // 同步文件
            if (hxof != null && !hxof.isEmpty()) {
                originalFilename = URLDecoder.decode(hxof, StandardCharsets.UTF_8);
            }
        }
        File dest = getFile(FileUtils.getSubDir(filename), filename);
        FileUtils.output(file.getInputStream(), new FileOutputStream(dest));// 复制文件到指定位置

        // 2. 处理meta
        FileMeta meta = new FileMeta(filename, originalFilename, dest.length(), tcDfsProperties.getDownloadUrl());
        if (tcDfsProperties.isAutoMd5()) {
            meta.getTags().put("md5", DigestUtils.md5DigestAsHex(new FileInputStream(dest)));
        }

        // 2.1 存放到本地文件
        FileUtils.writeMeta(new File(dest.getAbsoluteFile() + ".meta"), meta);

        // 2.2 存放到数据库
        // 2.3 存放到配置中心或者注册中心，比如zk

        // 3. 同步到backup
        if (needSync) {
            if (tcDfsProperties.isSyncBackup()) {
                try {
                    httpSyncer.sync(dest, tcDfsProperties.getBackupUrl(), originalFilename);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    // 同步处理失败时，进行异步处理（为了不阻塞主流程）
                    mqSyncer.sync(meta);
                }
            } else {
                mqSyncer.sync(meta);
            }
        }

        return filename;
    }

    private @NotNull File getFile(String subDir, String filename) {
        return new File(String.join(File.separator, tcDfsProperties.getPath(), subDir, filename));
    }

    @SneakyThrows
    @RequestMapping("/download")
    public void download(String name, HttpServletResponse response) {
        File file = getFile(FileUtils.getSubDir(name), name);

        // 加一些 response 的头信息
        response.setCharacterEncoding("UTF-8");
        response.setContentType(FileUtils.getMimeType(name));
        // response.setHeader("Content-Disposition", "attachment;filename=" + name);
        response.setHeader("Content-Length", String.valueOf(file.length()));

        FileUtils.output(file, response.getOutputStream());
    }

    @SneakyThrows
    @RequestMapping("/meta")
    public String meta(String name) {
        return FileUtils.readString(getFile(FileUtils.getSubDir(name), name));
    }
}
